package com.fanyue.modules.face.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.convert.ConverterRegistry;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.img.Img;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.*;
import cn.hutool.system.SystemUtil;
import com.alibaba.fastjson.JSON;
import com.arcsoft.face.*;
import com.arcsoft.face.enums.CompareModel;
import com.arcsoft.face.enums.ExtractType;
import com.arcsoft.face.toolkit.ImageFactory;
import com.arcsoft.face.toolkit.ImageInfo;
import com.fanyue.core.base.BaseServiceImpl;
import com.fanyue.core.cache.CoolCache;
import com.fanyue.core.constant.CommonConstant;
import com.fanyue.core.request.R;
import com.fanyue.modules.face.config.ArcsoftProperties;
import com.fanyue.modules.face.config.FaceEngineCenter;
import com.fanyue.modules.face.entity.AppUser;
import com.fanyue.modules.face.entity.AppUserFace;
import com.fanyue.modules.face.mapper.AppUserFaceMapper;
import com.fanyue.modules.face.mapper.AppUserMapper;
import com.fanyue.modules.face.service.FaceEngineService;
import com.fanyue.modules.face.support.ImageConvertBase64;
import com.fanyue.modules.face.vo.FaceSimilarVO;
import com.fanyue.modules.face.vo.FaceVO;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import java.io.File;
import java.io.IOException;
import java.util.*;


@Slf4j
@Service
@RequiredArgsConstructor
@EnableConfigurationProperties(ArcsoftProperties.class)
public class FaceEngineServiceImpl extends BaseServiceImpl<AppUserFaceMapper, AppUserFace> implements FaceEngineService {

	@Value("${file.upload-domain}")
	private String uploadDomain;
	@Value("${file.remote-path}")
	private String uploadFilePath;
	// private final FaceEngineCenter faceEngineCenter;
	private final ArcsoftProperties arcsoftProperties;
	private final AppUserFaceMapper baseMapper;
	private final AppUserMapper appUserMapper;
	private final CoolCache coolCache;

	@Override
	public R featureList() {
		//获取人脸解析引擎
		FaceEngine faceEngine = FaceEngineCenter.getFaceEngine();
		VersionInfo versionInfo = faceEngine.getVersion();
		String version = versionInfo.getVersion();
		log.info("（人脸查询）FaceEngine版本: {}", version);
		FaceSearchCount faceSearchCount = new FaceSearchCount();
		int faceCount = faceEngine.getFaceCount(faceSearchCount);
		return R.ok(faceCount);
	}

	@Override
	@Transactional(rollbackFor = Exception.class)
	public R extractFeature(String type, Long userId, MultipartFile file, String facePath) {
		//查询用户信息
		AppUser appUser = appUserMapper.selectOneById(userId);
		if (ObjectUtil.isEmpty(appUser)) {
			return R.error("未查询到用户信息，请确认");
		}

		// 构建文件目录
		String yyyyMM = DateUtil.format(new Date(), "yyyyMM");
		String dd = DateUtil.format(new Date(), "dd");
		String faceFilePath = arcsoftProperties.getDirectory() + "/face/image/" + File.separator + yyyyMM + File.separator + dd + File.separator + userId + ".jpg";
		boolean isWindows = System.getProperty("os.name").toLowerCase().contains("win");
		if (isWindows) {
			faceFilePath = "D:\\Data\\Temp\\face\\" + userId + ".jpg";
		}
		if (FileUtil.exist(faceFilePath)) {
			FileUtil.del(faceFilePath);
		}
		File faceFile = FileUtil.newFile(faceFilePath);

		switch (type) {
			case "multipartFile":
				try {
					FileUtil.writeFromStream(file.getInputStream(), faceFile, true);
				} catch (IOException e) {
					return R.error("解析人脸数据失败，请重新上传");
				}
				break;
			case "filePath":
				try {
					FileUtil.copy(facePath, faceFilePath, true);
				} catch (Exception e) {
					return R.error("解析人脸数据失败，请重新上传");
				}
				break;
		}
		//构建图片地址，返回结果为下载文件大小
		//String suffix = FileUtil.getSuffix(originalFilename);

		//获取人脸解析引擎
		FaceEngine faceEngine = FaceEngineCenter.getFaceEngine();
		VersionInfo versionInfo = faceEngine.getVersion();
		String version = versionInfo.getVersion();
		log.info("（人脸注册）FaceEngine版本: {}", version);
		//人脸检测
		ImageInfo imageInfo = ImageFactory.getRGBData(faceFile);
		List<FaceInfo> faceInfoList = new ArrayList<FaceInfo>();
		int detectFacesCode = faceEngine.detectFaces(imageInfo, faceInfoList);
		log.info("人脸检测状态:{}" , detectFacesCode == 0 ? "正常" : "异常");
		if (detectFacesCode != 0) {
			delFaceFile(faceFilePath);
			return R.error("系统人脸检测失败，请重新拍摄");
		}

		// 判断画面没有人脸，返回提示
		if (faceInfoList.size() == 0) {
			delFaceFile(faceFilePath);
			return R.error("系统未检测到人脸数据，请重新拍摄");
		}
		// 判断画面大于一张人脸，返回提示
		if (faceInfoList.size() > 1) {
			delFaceFile(faceFilePath);
			return R.error("系统检测到当前图像包含多个人脸数据，请重新拍摄");
		}

		//图片质量检测
		ImageQuality imageQuality = new ImageQuality();
		int qualityCode = faceEngine.imageQualityDetect(imageInfo, faceInfoList.get(0), 0, imageQuality);
		log.info("图像质量检测code: {}", qualityCode);
		log.info("图像质量分数: {}", imageQuality.getFaceQuality());
		if (qualityCode != 0) {
			delFaceFile(faceFilePath);
			return R.error("系统检测图像数据异常，请重新拍摄");
		}

		//人脸属性检测
		FunctionConfiguration configuration = new FunctionConfiguration();
		configuration.setSupportAge(true);
		configuration.setSupportGender(true);
		configuration.setSupportLiveness(true);
		configuration.setSupportMaskDetect(true);
		int processCode = faceEngine.process(imageInfo, faceInfoList, configuration);
		log.info("图像处理Code: {}", processCode);

		//获取人脸三维角度信息
		//List<Face3DAngle> face3DAngleList = new ArrayList<Face3DAngle>();
		Face3DAngle face3DAngle = faceInfoList.get(0).getFace3DAngle();
		log.info("face3D检测偏航角：{}", face3DAngle.getYaw());
		log.info("face3D检测横滚角：{}", face3DAngle.getRoll());
		log.info("face3D检测俯仰角：{}", face3DAngle.getPitch());
		/*if (face3DAngleList.get(0).getStatus() != 0) {
			return R.error("系统检测到当前图像人脸角度异常，请重新拍摄");
		}*/
		float yaw = face3DAngle.getYaw();
		float pitch = face3DAngle.getPitch();
		if ((-15f > yaw || yaw > 15f) || (-15f > pitch || pitch > 15f)) {
			delFaceFile(faceFilePath);
			return R.error("系统检测到当前图像人脸角度异常，请重新拍摄");
		}

		Integer livenessSign = arcsoftProperties.getLivenessSign();
		livenessSign = ObjectUtil.isEmpty(livenessSign) ? 0 : livenessSign;
		if (livenessSign == 1) {
			//活体检测
			List<LivenessInfo> livenessInfoList = new ArrayList<>();
			int livenessCode = faceEngine.getLiveness(livenessInfoList);
			log.info("活体检测code: {}", livenessCode);
			if (livenessCode != 0) {
				return R.error("活体检测失败，请重新进行识别");
			}
			log.info("活体：{}", livenessInfoList.get(0).getLiveness());

			/**
			 * 0：非真人； 1：真人；-1：不确定； -2：传入人脸数 > 1；-3: 人脸过小；-4: 角度过大；
			 * -5: 人脸超出边界；-6：深度图错误；-7：红外图过亮；-8：红外图过暗；-100：人脸质量错误
			 */
			if (CollUtil.isNotEmpty(livenessInfoList)) {
				int liveness = livenessInfoList.get(0).getLiveness();
				switch (liveness) {
					case 0:
						return R.error("系统检测到当前非真人，请进行真人识别");
					case -1:
						return R.error("系统检测到当前不确定非真人，请重新进行识别");
					case -2:
						return R.error("系统检测到当前传入人脸数大于1，请重新进行识别");
					case -5:
						return R.error("系统检测到当前人脸超出边界，请重新进行识别");
					case -100:
						return R.error("系统检测到当前人脸质量过低，请重新进行识别");
				}
			} else {
				return R.error("活体检测失败，请重新进行识别");
			}
		}

		//口罩检测
		List<MaskInfo> maskInfoList = new ArrayList<MaskInfo>();
		int maskCode = faceEngine.getMask(maskInfoList);
		log.info("口罩检测Code: {}", maskCode);
		if (maskCode != 0) {
			delFaceFile(faceFilePath);
			return R.error("系统检测口罩失败，请重新拍摄");
		}
		int maskSign = maskInfoList.get(0).getMask();
		log.info("检测口罩: {}", maskSign);


		// faceEngine.processIr(imageInfo, faceInfoList, configuration);

		//人像特征提取
		FaceFeature faceFeature = new FaceFeature();
		int featureCode = faceEngine.extractFaceFeature(
			imageInfo,
			faceInfoList.get(0),
			ExtractType.REGISTER,
			maskSign,
			faceFeature
		);
		log.info("特征值状态:{}" , featureCode == 0 ? "正常" : "异常");

		//人脸特征解析成功之后进行
		if (featureCode == 0) {

			String featureData = Convert.toStr(faceFeature.getFeatureData());
			//对解析的数据进行数据比对
			FaceVO faceVO = new FaceVO();
			faceVO.setFeatureData(featureData);
			if (StrUtil.isNotEmpty(facePath)) {
				faceVO.setType("extract4System");
			}
			R compareResult = compareFeature(faceVO);
			int code = Integer.parseInt(compareResult.get("code").toString());
			Object data = compareResult.get("data");
			if (code == 200) {
				ConverterRegistry registry = ConverterRegistry.getInstance();
				Map convert = registry.convert(Map.class, data);
				AppUser appUserInfo = registry.convert(AppUser.class, convert.get("userInfo"));
				if (!appUserInfo.getId().equals(userId)) {
					return R.error("检测到系统已存在当前人脸数据，匹配用户：" + appUserInfo.getName() + "，请确认后重新上传！");
				}
			}

			//更新人脸文件
			Img.from(FileUtil.file(faceFilePath))
				//压缩比率
				.setQuality(0.6)
				//.rotate(270)
				.write(FileUtil.file(faceFilePath));

			//判断人脸库是否有当前用户数据，有则更新
			AppUserFace dbUserFace = baseMapper.selectUserInfo(userId);
			if (ObjectUtil.isEmpty(dbUserFace)) {

				//寻找最后一条数据
				Integer searchId = baseMapper.selectLastSearchId();
				searchId = ObjectUtil.isEmpty(searchId) ? 1 : searchId + 1;

				// 存入人脸数据库
				AppUserFace userFace = new AppUserFace();
				userFace.setUserId(userId);
				userFace.setSearchId(searchId);
				userFace.setFaceFilePath(faceFilePath);
				userFace.setFeature(featureData);
				userFace.setNote(userId + "-人脸数据");
				userFace.setIsSync(0);
				this.save(userFace);

				appUser.setFaceId(userFace.getId());

				//注册人脸信息
				ArrayList<FaceFeatureInfo> faceFeatureInfoList = new ArrayList<>();
				FaceFeatureInfo faceFeatureInfo = new FaceFeatureInfo();
				faceFeatureInfo.setSearchId(searchId);
				faceFeatureInfo.setFaceTag(userId + "用户人脸数据, 对应SearchId为：" + searchId);
				faceFeatureInfo.setFeatureData(faceFeature.getFeatureData());
				faceFeatureInfoList.add(faceFeatureInfo);
				int registerCode = faceEngine.registerFaceFeature(faceFeatureInfo);
				if (registerCode != 0) {
					delFaceFile(faceFilePath);
					return R.error("系统注册人脸信息失败，请重新操作");
				}
				log.info("人脸信息注册状态:{}", registerCode);
				FaceSearchCount faceSearchCount = new FaceSearchCount();
				int faceCount = faceEngine.getFaceCount(faceSearchCount);
				log.info("当前引擎缓存人脸数量：{}", faceCount);
			} else {
				dbUserFace.setIsSync(0);
				dbUserFace.setFeature(featureData);
				dbUserFace.setFaceFilePath(faceFilePath);
				this.updateById(dbUserFace);

				appUser.setFaceId(dbUserFace.getId());

				//更新人脸信息
				FaceFeatureInfo faceFeatureInfo = new FaceFeatureInfo();
				int faceFeatureInfoCode = faceEngine.getFaceFeature(dbUserFace.getSearchId(), faceFeatureInfo);
				if (faceFeatureInfoCode == 0) {
					//FaceFeatureInfo faceFeatureInfo = new FaceFeatureInfo();
					//faceFeatureInfo.setSearchId(dbUserFace.getSearchId());
					//faceFeatureInfo.setFaceTag(userId + "用户人脸数据, 对应SearchId为：" + dbUserFace.getSearchId());
					faceFeatureInfo.setFeatureData(faceFeature.getFeatureData());
					int updateCode = faceEngine.updateFaceFeature(faceFeatureInfo);
					log.info("更新返回：{}", updateCode);
				} else {
					//重新注册人脸信息
					faceFeatureInfo.setSearchId(dbUserFace.getSearchId());
					faceFeatureInfo.setFaceTag("用户人脸数据, 对应SearchId为：" + dbUserFace.getSearchId());
					faceFeatureInfo.setFeatureData(faceFeature.getFeatureData());
					int registerCode = faceEngine.registerFaceFeature(faceFeatureInfo);
					//faceEngine.removeFaceFeature()
					if (registerCode != 0) {
						delFaceFile(faceFilePath);
						return R.error("系统注册人脸信息失败，请重新操作");
					}
					FaceSearchCount faceSearchCount = new FaceSearchCount();
					int faceCount = faceEngine.getFaceCount(faceSearchCount);
					log.info("重新注册-引擎缓存人脸数量：{}", faceCount);
				}
				/*if (updateCode != 0) {
					return R.error("系统更新人脸信息失败，请重新操作");
				}*/
			}
			//更新用户人脸数据
			appUserMapper.update(appUser);

			//将人脸数据存入redis中
			String cacheKey = CommonConstant.USER_FACE_ENGINE + arcsoftProperties.getAppName() + ":" + userId;
			// String cacheKey = CommonConstant.USER_FACE_ENGINE + ":" + userId;
			coolCache.set(cacheKey, featureData);
		} else {
			delFaceFile(faceFilePath);
			return R.error("人脸解析失败，请重新拍摄");
		}
		return R.ok("人脸数据解析成功");
	}

	@Override
	public R singleExtractFeature(String type, File file) {
		//获取人脸解析引擎
		FaceEngine faceEngine = FaceEngineCenter.getFaceEngine();

		ImageInfo imageInfo = ImageFactory.getRGBData(file);
		//人脸检测
		List<FaceInfo> faceInfoList = new ArrayList<>();
		int detectFacesCode = faceEngine.detectFaces(imageInfo, faceInfoList);
		log.info("人脸检测Code:{}" , detectFacesCode);

		if (CollUtil.isEmpty(faceInfoList)) {
			return R.error("人脸检测失败，请重新进行识别");
		}

		//图片质量检测
		ImageQuality imageQuality = new ImageQuality();
		int qualityCode = faceEngine.imageQualityDetect(imageInfo, faceInfoList.get(0), 0, imageQuality);
		log.info("图像质量检测code: {}", qualityCode);
		log.info("图像质量分数: {}", imageQuality.getFaceQuality());

		// 增加图片质量判断返回
		/*BaseConfigure qualityConfigure = null;
		switch (type) {
			case SystemConstant.FACE_RECOGNITION_FOR_DOUBLE:
				qualityConfigure = baseConfigureMapper.selectConfigureBySingleKey(SystemConstant.FACE_DOUBLE_QUALITY_SCORE);
				break;
			default:
				qualityConfigure = baseConfigureMapper.selectConfigureBySingleKey(SystemConstant.FACE_FILE_QUALITY_SCORE);
				break;
		}
		float qualityScore = ObjectUtil.isEmpty(qualityConfigure) ? 0f : Float.parseFloat(qualityConfigure.getConVal());
		if (imageQuality.getFaceQuality() < qualityScore) {
			return R.error("人脸检测识别图像质量过低，请重新进行识别");
		}*/

		//人脸属性检测
		FunctionConfiguration configuration = new FunctionConfiguration();
		configuration.setSupportAge(true);
		configuration.setSupportGender(true);
		configuration.setSupportLiveness(true);
		//configuration.setSupportIRLiveness(true);
		configuration.setSupportMaskDetect(true);
		int processCode = faceEngine.process(imageInfo, faceInfoList, configuration);
		log.info("人脸属性检测code: {}", processCode);

		// 判断是否开启活体检测
		Integer livenessSign = arcsoftProperties.getLivenessSign();
		livenessSign = ObjectUtil.isEmpty(livenessSign) ? 0 : livenessSign;
		if (livenessSign == 1) {
			//活体检测
			List<LivenessInfo> livenessInfoList = new ArrayList<>();
			int livenessCode = faceEngine.getLiveness(livenessInfoList);
			log.info("活体检测code: {}", livenessCode);
			if (livenessCode != 0) {
				return R.error("活体检测失败，请重新进行识别");
			}
			log.info("活体：{}", livenessInfoList.get(0).getLiveness());

			/**
			 * 0：非真人； 1：真人；-1：不确定； -2：传入人脸数 > 1；-3: 人脸过小；-4: 角度过大；
			 * -5: 人脸超出边界；-6：深度图错误；-7：红外图过亮；-8：红外图过暗；-100：人脸质量错误
			 */
			if (CollUtil.isNotEmpty(livenessInfoList)) {
				int liveness = livenessInfoList.get(0).getLiveness();
				switch (liveness) {
					case 0:
						return R.error("系统检测到当前非真人，请进行真人识别");
					case -1:
						return R.error("系统检测到当前不确定非真人，请重新进行识别");
					case -2:
						return R.error("系统检测到当前传入人脸数大于1，请重新进行识别");
					case -5:
						return R.error("系统检测到当前人脸超出边界，请重新进行识别");
					case -100:
						return R.error("系统检测到当前人脸质量过低，请重新进行识别");
				}
			} else {
				return R.error("活体检测失败，请重新进行识别");
			}
		}

		//获取人脸三维角度信息
		/*
		List<Face3DAngle> face3DAngleList = new ArrayList<Face3DAngle>();
		Face3DAngle face3DAngle = faceInfoList.get(0).getFace3DAngle();
		log.info("face3D检测偏航角：{}", face3DAngle.getYaw());
		log.info("face3D检测横滚角：{}", face3DAngle.getRoll());
		log.info("face3D检测俯仰角：{}", face3DAngle.getPitch());
		if (face3DAngleList.get(0).getStatus() != 0) {
			return R.error("系统检测到当前图像人脸角度异常，请重新拍摄");
		}
		float yaw = face3DAngle.getYaw();
		float pitch = face3DAngle.getPitch();
		*/

		//口罩检测
		List<MaskInfo> maskInfoList = new ArrayList<MaskInfo>();
		int maskCode = faceEngine.getMask(maskInfoList);
		log.info("图片识别检测口罩: {}", maskCode);

		int maskSign = -1;
		if (CollUtil.isNotEmpty(maskInfoList)) {
			maskSign = maskInfoList.get(0).getMask();
			log.info("检测口罩: {}", maskSign);
		}

		//人像特征提取
		FaceFeature faceFeature = new FaceFeature();
		int featureCode = faceEngine.extractFaceFeature(
			imageInfo,
			faceInfoList.get(0),
			ExtractType.REGISTER,
			maskSign,
			faceFeature
		);
		log.info("特征值状态:{}" , featureCode == 0 ? "正常" : "异常");

		//人脸特征解析成功之后进行
		if (featureCode == 0) {
			return R.ok(faceFeature);
		}
		return null;
	}

	@Override
	public R selectFeature(Long cid, MultipartFile file) {
		//构建图片地址，返回结果为下载文件大小
		String originalFilename = file.getOriginalFilename();
		String uuid = IdUtil.nanoId();
		//String faceFilePath = "/lecent_v1/face/image/temp/" + uuid + ".jpg";
		String faceFilePath = arcsoftProperties.getDirectory() + "/face/image/temp/" + uuid + ".jpg";
		boolean isWindows = System.getProperty("os.name").toLowerCase().contains("win");
		if (isWindows) {
			faceFilePath = "C:\\Data\\Temp\\face\\" + uuid + ".jpg";
		}
		if (FileUtil.exist(faceFilePath)) {
			FileUtil.del(faceFilePath);
		}
		File tempFaceFile = FileUtil.touch(faceFilePath);
		try {
			FileUtil.writeFromStream(file.getInputStream(), tempFaceFile, true);
		} catch (IOException e) {
			e.printStackTrace();
		}

		//获取人脸解析引擎
		FaceEngine faceEngine = FaceEngineCenter.getFaceEngine();

		ImageInfo imageInfo = ImageFactory.getRGBData(tempFaceFile);
		//人脸检测
		List<FaceInfo> faceInfoList = new ArrayList<FaceInfo>();
		int detectFacesCode = faceEngine.detectFaces(imageInfo, faceInfoList);
		log.info("人脸检测状态:{}" , detectFacesCode == 0 ? "正常" : "异常");
		if (detectFacesCode != 0) {
			return R.error("系统人脸检测失败，请重新拍摄");
		}

		// 判断画面没有人脸，返回提示
		if (faceInfoList.size() == 0) {
			return R.error("系统未检测到人脸数据，请重新拍摄");
		}
		// 判断画面大于一张人脸，返回提示
		if (faceInfoList.size() > 1) {
			return R.error("系统检测到当前图像包含多个人脸数据，请重新拍摄");
		}

		//图片质量检测
		ImageQuality imageQuality = new ImageQuality();
		int qualityCode = faceEngine.imageQualityDetect(imageInfo, faceInfoList.get(0), 0, imageQuality);
		log.info("图像质量检测code: {}", qualityCode);
		log.info("图像质量分数: {}", imageQuality.getFaceQuality());
		if (qualityCode != 0) {
			return R.error("系统检测图像数据异常，请重新拍摄");
		}

		//人脸属性检测
		FunctionConfiguration configuration = new FunctionConfiguration();
		configuration.setSupportAge(true);
		configuration.setSupportGender(true);
		configuration.setSupportLiveness(true);
		configuration.setSupportMaskDetect(true);
		int processCode = faceEngine.process(imageInfo, faceInfoList, configuration);

		//获取人脸三维角度信息
		//List<Face3DAngle> face3DAngleList = new ArrayList<Face3DAngle>();
		Face3DAngle face3DAngle = faceInfoList.get(0).getFace3DAngle();
		log.info("face3D检测偏航角：{}", face3DAngle.getYaw());
		log.info("face3D检测横滚角：{}", face3DAngle.getRoll());
		log.info("face3D检测俯仰角：{}", face3DAngle.getPitch());
		/*if (face3DAngleList.get(0).getStatus() != 0) {
			return R.error("系统检测到当前图像人脸角度异常，请重新拍摄");
		}*/
		float yaw = face3DAngle.getYaw();
		float pitch = face3DAngle.getPitch();
		if ((-15f > yaw || yaw > 15f) || (-15f > pitch || pitch > 15f)) {
			return R.error("系统检测到当前图像人脸角度异常，请重新拍摄");
		}

		//口罩检测
		List<MaskInfo> maskInfoList = new ArrayList<MaskInfo>();
		int maskCode = faceEngine.getMask(maskInfoList);
		if (maskCode != 0) {
			return R.error("系统检测口罩失败，请重新拍摄");
		}
		int maskSign = maskInfoList.get(0).getMask();
		log.info("检测口罩: {}", maskSign);

		//人像特征提取
		FaceFeature faceFeature = new FaceFeature();
		int featureCode = faceEngine.extractFaceFeature(
			imageInfo,
			faceInfoList.get(0),
			ExtractType.REGISTER,
			maskSign,
			faceFeature
		);
		log.info("特征值状态:{}" , featureCode == 0 ? "正常" : "异常");

		//人脸特征解析成功之后进行
		Map<String, Object> kv = MapUtil.newHashMap();
		if (featureCode == 0) {
			String featureData = Convert.toStr(faceFeature.getFeatureData());
			log.info("当前人脸特征值:{}" , featureData);

			//搜索最相似人脸
			SearchResult searchResult = new SearchResult();
			int searchCode = faceEngine.searchFaceFeature(faceFeature, CompareModel.LIFE_PHOTO, searchResult);

			log.info("最相似人脸searchCode: {}", searchCode);
			log.info("最相似人脸结果: {}", JSON.toJSONString(searchResult));

			if (searchCode != 0) {
				R.error("人脸解析引擎搜索人脸数据失败，请重新操作");
			}
			if (ObjectUtil.isEmpty(searchResult) || ObjectUtil.isEmpty(searchResult.getFaceFeatureInfo())) {
				R.error("人脸解析引擎搜索人脸数据失败，请重新操作");
			}

			int searchId = searchResult.getFaceFeatureInfo().getSearchId();
			log.info("最相似人脸Id: {}", searchId);

			//根据SearchId查询用户id
			Long userId = baseMapper.selectUserIdBySearchId(searchId);

			//删除文件
			if (FileUtil.exist(faceFilePath)) {
				FileUtil.del(faceFilePath);
			}

			kv.put("featureData", featureData);
			kv.put("searchId", searchId);
			kv.put("userId", userId);
		}
		return R.ok(kv);
	}

	@Override
	public R compareFeature(FaceVO faceVO) {

		Map<String, Object> result = new HashMap<>();
		Long userId = null;
		Boolean useCache = true;

		// 根据cid获取租户id
		// String tenantId = canteenMapper.selectTenantIdByCid(faceVO.getCid());
		// faceVO.setTenantId(tenantId);
		//获取人脸识别引擎
		//FaceEngine faceEngine = FaceEngineConfiguration.getFaceEngine(faceVO.getCid());
		//byte[] featureBytes = ConvertFeatureUtils.stringToBytes(faceVO.getFeatureData());
		String replace = faceVO.getFeatureData().replace("[", "").replace("]", "");
		byte[] featureBytes = Convert.toPrimitiveByteArray(replace);

		FaceEngine faceEngine = FaceEngineCenter.getFaceEngine();
		VersionInfo versionInfo = faceEngine.getVersion();
		String version = versionInfo.getVersion();
		log.info("（人脸对比）FaceEngine版本: {}", version);

		FaceFeature faceFeature = new FaceFeature();
		faceFeature.setFeatureData(featureBytes);

		// 获取人脸匹配分数
		Float maxScore = arcsoftProperties.getMaxScore();
		maxScore = ObjectUtil.isEmpty(maxScore) ? 0.8f : maxScore;

		log.info("本次匹配的最大分数值为：{}", maxScore);
		FaceSimilarVO faceSimilarData = new FaceSimilarVO();
		faceSimilarData.setMaxSimilarScore(0f);
		result.put("faceSimilar", faceSimilarData);

		//搜索最相似人脸
		SearchResult searchResult = new SearchResult();
		int searchCode = faceEngine.searchFaceFeature(faceFeature, CompareModel.LIFE_PHOTO, searchResult);
		log.info("引擎比对人脸比对Code：{}", searchCode);
		if (searchCode == 0) {
			log.info("------------使用引擎进行比对中...");
			float maxSimilar = searchResult.getMaxSimilar();
			int searchId = searchResult.getFaceFeatureInfo().getSearchId();
			log.info("***引擎比对最匹配人脸Id: {}", searchResult.getFaceFeatureInfo().getSearchId());
			log.info("***引擎比对最匹配人脸分数: {}", searchResult.getMaxSimilar());
			if (maxSimilar >= maxScore) {
				//根据SearchId查询用户id
				userId = baseMapper.selectUserIdBySearchId(searchId);
				AppUser appUserInfo = appUserMapper.selectOneById(userId);
				if (ObjectUtil.isNotEmpty(appUserInfo)) {
					useCache = false;
					faceSimilarData.setUserId(userId);
					faceSimilarData.setMaxSimilarScore(maxSimilar);
				}
			}
		}

		// 设置匹配的用户id集合
		List<FaceVO> userFaceList = new ArrayList<>();

		if (useCache) {
			log.info("------------使用缓存数据进行比对中...");
			//byte[] targetFeatureArray = Convert.toPrimitiveByteArray(faceVO.getFeatureData());

			//获取人脸库数据
			//byte[] faceFeatureData = new byte[]{0, -128, -6, 68, 0, 0, -96, 65, 37, 75, 11, 60, 107, 24, -84, -70, -30, 88, -78, -67, 69, 12, -106, 58, 93, 121, -126, 61, -50, 3, 45, -68, -17, 72, -51, -67, 78, -124, -66, -68, -53, 12, -83, -67, -51, 5, 113, -68, 67, 87, 92, 61, -55, -44, -52, -68, 112, -28, -120, -67, -41, -38, 69, -67, -123, 80, -59, 60, -54, -114, -120, 61, -113, 33, 87, -68, 54, -40, -33, -67, -24, 124, -10, 61, -116, 26, -116, 61, -34, 121, 116, -67, -43, -113, -66, 60, 93, 80, -73, -68, -103, 2, 35, -68, -94, -63, 26, 62, 27, -27, -127, 61, 64, 62, -112, -68, -105, 58, 68, 61, -9, 126, 109, -67, 62, -23, 16, 62, -11, 116, -79, 61, 32, 80, -41, 61, 87, 126, 124, -67, -90, -122, -59, 60, -107, -38, 64, -68, 37, 111, -43, 60, -64, 94, -92, -67, 127, -109, -48, 59, -128, -105, 41, -67, -118, 120, 16, 61, -33, -126, -113, -67, -38, 48, -117, 61, 75, 67, -77, -68, -49, 47, 115, 61, 57, 67, 84, -68, -7, 87, -26, -67, 67, 53, -5, -68, 114, -44, -72, 60, 8, -30, 125, -67, 113, 16, 88, -67, 70, -85, 86, 61, -94, 12, 33, -67, 27, 114, -78, 61, 67, -89, -25, -67, 43, 107, 4, -69, -24, -122, -43, -68, 10, -57, 38, -67, -43, -108, 111, -67, -10, 19, -26, -67, -61, 109, 100, 59, 75, -79, -103, 61, 50, 14, 10, 61, -86, 28, -82, -68, -58, 0, 68, 62, 81, -12, -53, 61, -65, -126, -62, 61, 89, 24, 40, -66, 86, -122, -48, 60, 71, 50, -120, 60, -8, -114, 15, 59, -102, 27, 52, 61, -43, 2, 99, 60, -26, 2, -44, 61, -101, -114, 119, -67, 104, 35, 87, 61, 83, 13, 115, 61, -115, 69, -21, 60, 88, -14, -123, 61, -21, -128, 33, -67, -38, -44, -49, -67, -74, -38, -119, -67, 75, -25, 120, 61, 62, -68, -119, 58, -119, -85, 113, 61, -2, 127, -86, -68, -126, -95, -120, -67, -6, -4, -30, 61, -121, -81, 45, -68, -6, -84, 74, -66, 85, -61, -124, 61, 113, -94, -79, -67, -91, 79, 115, -67, -46, 113, -97, -67, 5, 11, 95, 61, -31, 107, 72, 59, 25, 10, -13, 59, 113, 6, 14, -66, 96, -97, 28, 61, 111, 124, -88, -68, -33, 82, 24, 59, -10, 88, 59, 60, -97, 33, 11, 60, -122, -6, 121, -67, 109, 91, -101, -67, -93, -63, -78, 61, 56, -19, -89, 60, 23, -121, -117, 61, 83, -95, 1, 62, -26, 98, 98, -67, 67, -74, 9, 61, -19, 49, 31, 61, 115, 77, 84, -67, 65, 4, 42, 62, 97, -103, 89, -67, 114, -95, 111, 61, 2, -81, 10, 62, 81, -4, -122, 60, 75, -107, -128, -67, 34, -40, -109, -67, 24, 119, 55, -67, 9, 40, -65, -69, -5, -97, -101, -70, 30, -93, -61, 61, -97, 4, -47, -70, 114, 66, 91, 61, -109, -77, -87, 61, 30, 25, -79, -68, -58, 110, -92, 61, 113, 123, -44, 60, -57, 25, 49, 61, -91, 44, -64, 61, -122, -103, -92, -67, -15, -92, 64, -68, 33, 16, -63, -69, -112, -10, -12, 60, 81, -110, -4, -67, -40, -116, 72, -67, 6, 21, 100, -67, 57, 63, -126, 60, 38, -81, 77, -68, 87, -121, 93, 60, -87, 103, 1, 62, 7, -98, 92, 61, -63, 85, 101, 60, -98, -1, 119, 60, 47, 96, 107, -67, 30, -65, 78, -68, 80, -37, -40, 61, 97, 52, 49, -69, -95, -12, -30, -67, 49, 48, -10, 61, 78, 2, 50, 60, -103, -46, -62, -68, -1, 83, 45, 61, 92, 58, 3, -67, -51, 107, -110, 60, -70, 16, -61, 61, 81, 24, -74, -67, -25, -124, 4, -66, 118, 107, -27, -68, 16, -46, 117, -67, 22, -11, -100, -67, 73, 38, -120, 61, 2, -63, 10, 59, -110, 50, 48, 60, 48, 77, 31, -67, -3, 75, -79, 60, -103, 21, -36, -68, -104, -89, -62, 61, 82, 73, -21, 61, -102, 4, 52, -67, -33, -38, -69, 61, -43, -51, 32, 61, -5, -71, -72, -67, -71, -42, 49, -69, -31, -26, -108, 61, 65, 109, -96, 61, -41, -53, 28, -66, 105, 91, -96, -67, -55, -12, -128, 61, 29, 117, -43, -69, -56, 48, 44, 61, 72, -56, -85, 61, -83, 4, 83, 61, -117, -78, -40, -67, -23, 49, -116, 60, -89, 127, -89, -67, 40, -69, -60, 61, -83, -89, -36, 60, -40, -7, 106, 60, 23, 122, 79, -67, -40, 78, 62, -67, 8, 72, 52, 60, 10, -90, 14, 62, -112, 117, -35, -67, -66, 28, -120, -67, -26, -26, 62, -67, -61, 40, -125, -67, -26, 68, 38, 61, -86, 96, -26, -68, 37, 32, 48, -68, 9, 126, -108, -67, 58, 1, 55, -67, -41, -110, 27, -67, 74, 70, -64, -67, 93, 53, -83, 61, -64, -20, -58, 61, -51, 88, -88, -67, 31, -19, -66, 60, 94, -112, 118, -67, -9, 75, -122, 61, -29, -100, 54, -68, -30, 118, 5, 60, 18, 74, 17, -68, 9, 38, -112, 61, -34, -70, 72, 59, 23, 0, -123, -67, -103, -16, 56, -67, -30, -59, 13, -67, -109, 46, -90, -67, 114, 82, -75, 61, 35, 111, -39, -69, -61, -108, -97, 60, -100, 112, -120, 60, -111, 94, -67, 61, 85, -90, 29, 61, -51, -120, 127, 61, -11, 57, -64, -67, -53, -60, -34, 61, -53, 84, 82, -68, 31, 108, -39, -67, -57, -14, 17, -67, 27, -60, -35, -68, -91, -7, 38, 61, -26, 80, 60, -68, -89, 45, 100, -68, 109, -98, -20, 60, -14, -120, -71, -68, -57, -113, -60, 60, -83, 16, 26, 61, -77, 86, 2, -67, 58, 89, 111, 61, 97, -17, -7, 61, -53, 28, 36, -70, 101, -6, 108, 61, -45, -98, -12, 61, -112, -79, -108, -67, 8, -119, -43, 61, 111, 59, -94, 59, 113, 74, 4, 61, -107, 105, -114, 60, 71, 92, -76, -69, -42, 122, -92, -68, -33, -98, -16, 59, 114, -105, -85, -68, 55, -66, -15, 59};

			Set<String> faceRedisKeys = coolCache.keys(CommonConstant.USER_FACE_ENGINE + arcsoftProperties.getAppName() + ":*");
			log.info("缓存人脸共计数量：{}", faceRedisKeys.size());

			if (faceRedisKeys.size() > 0) {
				//开始执行循环比较
				for (String faceRedisKey : faceRedisKeys) {
					String featureData = coolCache.get(faceRedisKey).toString();
					//byte[] sourceFeatureData = Convert.hexToBytes(featureData);
					featureData = featureData.replace("[", "").replace("]", "");
					byte[] sourceFeatureData = Convert.toPrimitiveByteArray(featureData);
					FaceFeature sourceFaceFeature = new FaceFeature();
					sourceFaceFeature.setFeatureData(sourceFeatureData);
					FaceSimilar faceSimilar = new FaceSimilar();
					int code = faceEngine.compareFaceFeature(faceFeature, sourceFaceFeature, faceSimilar);
					Long similarUserId = Convert.toLong(StrUtil.subAfter(faceRedisKey, ":", true));
					//log.info("缓存比对的人脸分数：{}", faceSimilar.getScore());
					if (code == 0) {
						// 获取最大的人脸相似分数
						if (faceSimilar.getScore() > faceSimilarData.getMaxSimilarScore()) {
							faceSimilarData.setUserId(similarUserId);
							faceSimilarData.setMaxSimilarScore(faceSimilar.getScore());
						}
						//取分数大于阈值的人脸
						if (faceSimilar.getScore() >= maxScore) {
							log.info("缓存比对合规的用户id：{}", similarUserId);
							log.info("缓存比对合规的人脸分数：{}", faceSimilar.getScore());
							FaceVO face = new FaceVO();
							face.setUserId(similarUserId);
							face.setScore(faceSimilar.getScore());
							userFaceList.add(face);
						}
					}
				}
			}

			if (userFaceList.size() > 0) {
				log.info("缓存比对共计匹配到：{} 人", userFaceList.size());
				//最后取最大的一个判别人脸检测用户
				FaceVO faceVOInfo = userFaceList.stream()
					.max(Comparator.comparing(item -> item.getScore()))
					.get();

				log.info("***缓存比对最匹配人脸信息id：{}", faceVOInfo.getUserId());
				log.info("***缓存比对最匹配人脸信息分数：{}", faceVOInfo.getScore());

				userId = faceVOInfo.getUserId();

				if (ObjectUtil.isNotEmpty(faceVOInfo)) {
					//注册人脸信息
					AppUserFace appUserFace = baseMapper.selectUserInfo(userId);
					Integer searchId = 0;
					if (ObjectUtil.isEmpty(appUserFace)) {
						//寻找最后一条数据
						searchId = baseMapper.selectLastSearchId();
						searchId = ObjectUtil.isEmpty(searchId) ? 1 : searchId + RandomUtil.randomInt(1, 10);
					} else {
						searchId = appUserFace.getSearchId();
					}
					FaceFeatureInfo faceFeatureInfo = new FaceFeatureInfo();
					faceFeatureInfo.setSearchId(searchId);
					faceFeatureInfo.setFaceTag(userId + "用户人脸数据, 对应SearchId为：" + searchId);
					faceFeatureInfo.setFeatureData(faceFeature.getFeatureData());

					int registerCode = faceEngine.registerFaceFeature(faceFeatureInfo);
					if (registerCode != 0) {
						return R.ok()
								.put("code", 350)
								.put("code", 2)
								.put("message", "系统登记人脸信息失败，请重新操作");
					}
				}
			} else {
				//return R.error("引擎缓存均未匹配到对应用户信息，请联系管理员重新进行人脸上传");
				return R.ok()
						.put("code", 450)
						.put("data", result)
						.put("message", "未匹配到对应用户信息，请联系管理员重新进行人脸上传");
			}
		}

		//查询配置的用户
		AppUser userInfo = appUserMapper.selectOneById(userId);
		if (ObjectUtil.isEmpty(userInfo)) {
			return R.error("未查询到对应用户信息");
		}
		if (ObjectUtil.isEmpty(userInfo) && userFaceList.size() > 1) {
			coolCache.del(CommonConstant.USER_FACE_ENGINE  + arcsoftProperties.getAppName() + ":"+ userId);
			// 重新调用人脸识别
			compareFeature(faceVO);
		} else {
			result.put("userInfo", userInfo);
			return R.ok(result);
		}
		return R.error("未匹配到对应用户信息");
	}

	@Override
	public R compareFeatureBase64(FaceVO faceVO) {
		// Base64转图片
		// BufferedImage bufferedImage = ImgUtil.toImage(faceVO.getBase64());
		// String filePath = "/lecent_v1/face/image/temp/" + RandomUtil.randomNumbers(10) + ".png";
		String filePath = arcsoftProperties.getDirectory() + "/face/image/temp/" + RandomUtil.randomNumbers(10) + ".png";
		if (SystemUtil.getOsInfo().isWindows()) {
			filePath = "D:" + File.separator + "face" + File.separator + "image" + File.separator + "temp" + File.separator + RandomUtil.randomNumbers(10) + ".png";
		}
		File file = FileUtil.newFile(filePath);
		ImageConvertBase64.toImage(faceVO.getBase64(), file);

		/*R extractFeature = singleExtractFeature(file);
		FileUtil.del(filePath);*/

		// 设置默认为单面屏解析
		if (StrUtil.isEmpty(faceVO.getType())) {
			faceVO.setType("single");
		}
		log.info("当前执行base64图片解析：针对设备类型 - {}", faceVO.getType());
		R extractFeature = singleExtractFeature(faceVO.getType(), file);
		FileUtil.del(filePath);

		int code = Integer.parseInt(extractFeature.get("code").toString());
		Object data = extractFeature.get("data");
		if (code == 200) {
			ConverterRegistry converterRegistry = ConverterRegistry.getInstance();
			FaceFeature faceFeature = converterRegistry.convert(FaceFeature.class, data);
			if (ObjectUtil.isNotEmpty(faceFeature)) {
				String featureStr = Convert.toStr(faceFeature.getFeatureData());
				faceVO.setFeatureData(featureStr);
				return compareFeature(faceVO);
			}
		} else {
			return extractFeature;
		}
		return R.error("人脸识别失败，请重新进行识别");
	}


	public Boolean checkFacePosition(ImageInfo imageInfo, FaceInfo faceInfo) {
		Boolean checkSign = false;
		//获取图片宽高
		Integer imageHeight = imageInfo.getHeight();
		Integer imageWidth = imageInfo.getWidth();
		//计算图片中心点位置
		int imageCH = NumberUtil.div(String.valueOf(imageHeight), String.valueOf(2), 0).intValue();
		int imageCW = NumberUtil.div(String.valueOf(imageWidth), String.valueOf(2), 0).intValue();
		//获取人脸区域数据
		Rect rect = faceInfo.getRect();
		int rectTop = rect.getTop();
		int rectRight = rect.getRight();
		int rectBottom = rect.getBottom();
		int rectLeft = rect.getLeft();
		//计算人脸区域宽高
		int rectHeight = rectTop + rectBottom;
		int rectWeight = rectLeft + rectRight;
		//计算图片中心点位置
		int rectCH = NumberUtil.div(String.valueOf(rectHeight), String.valueOf(2), 0).intValue();
		int rectCW = NumberUtil.div(String.valueOf(rectWeight), String.valueOf(2), 0).intValue();

		//计算人脸区域和图片比例
		double hScale = NumberUtil.div(String.valueOf(rectHeight), String.valueOf(imageHeight), 1).doubleValue();
		double wScale = NumberUtil.div(String.valueOf(rectWeight), String.valueOf(imageWidth), 1).doubleValue();

		//规定边界区域
		return checkSign;
	}

	@Override
	public R deleteFeature(Long userId) {
		// 根据userId查询face信息
		AppUserFace appUserFace = baseMapper.selectUserInfo(userId);
		if (ObjectUtil.isNotEmpty(appUserFace)) {
			// 获取人脸解析引擎
			FaceEngine faceEngine = FaceEngineCenter.getFaceEngine();
			FaceFeatureInfo faceFeatureInfo = new FaceFeatureInfo();
			int faceFeatureInfoCode = faceEngine.getFaceFeature(appUserFace.getSearchId(), faceFeatureInfo);
			if (faceFeatureInfoCode == 0) {
				faceFeatureInfo.setSearchId(appUserFace.getSearchId());
				String feature = appUserFace.getFeature();
				String replace = feature.replace("[", "").replace("]", "");
				byte[] featureBytes = Convert.toPrimitiveByteArray(replace);
				Arrays.fill(featureBytes, (byte) 0);
				faceFeatureInfo.setFeatureData(featureBytes);
				int updateResult = faceEngine.updateFaceFeature(faceFeatureInfo);
				log.info("移除更新人脸Code：{}", updateResult);
				int removeResult = faceEngine.removeFaceFeature(appUserFace.getSearchId());
				log.info("移除人脸Code：{}", removeResult);
				log.info("移除人脸状态：{}", removeResult == 0 ? "正常" : "异常");
				if (removeResult != 0) {
					return R.error("删除人脸引擎信息失败");
				}
			}
			// 删除缓存信息
			coolCache.del(CommonConstant.USER_FACE_ENGINE + userId);
			/*if (!delSign) {
				return R.error("删除人脸缓存信息失败");
			}*/
			// 删除人脸文件
			FileUtil.del(appUserFace.getFaceFilePath());
			// 删除face信息
			baseMapper.deleteById(appUserFace);
		}
		return R.ok("删除人脸信息成功");
	}

	public void delFaceFile(String faceFilePath) {
		if (FileUtil.exist(faceFilePath)) {
			FileUtil.del(faceFilePath);
		}
	}

	@Override
	public R confirm(List<Long> ids) {
		baseMapper.confirmUserFace(ids);
		return R.ok("操作成功");
	}

	@Override
	public R getUserFaceData(Long userId) {
		AppUserFace appUserFace = baseMapper.selectUserInfo(userId);
		//处理人脸地址返回
		if (ObjectUtil.isNotEmpty(appUserFace)) {
			String faceFilePath = appUserFace.getFaceFilePath();
			String faceUrl = "";
			//判断当前环境
			boolean isWindows = System.getProperty("os.name").toLowerCase().contains("win");
			if (isWindows) {
				faceUrl = "http://gn9bsv.i996.me";
				faceFilePath = StrUtil.subAfter(faceFilePath, "\\Temp\\", true);
				faceFilePath = faceFilePath.replaceAll("\\\\", "/");
			} else {
				faceUrl = "http://face-file.hongshuocy.cn";
				faceFilePath = StrUtil.subAfter(faceFilePath, "/image/", true);
			}
			faceUrl = faceUrl + "/" + faceFilePath;
			// appUserFace.setFaceUrl(faceUrl);
			appUserFace.setFeature(null);
			appUserFace.setFaceFilePath(null);
		}
		return R.ok(appUserFace);
	}

}
